package im.composer.generators;

import org.jaudiolibs.audioservers.AudioConfiguration;
import org.tritonus.share.sampled.FloatSampleBuffer;

import im.composer.audio.engine.Source;
import im.composer.vapu.VAPU;
import jass.engine.BufferNotAvailableException;

public class Butter2LowFilter extends VAPU {

	/** State of filter. */
	private float[] yt_1, yt_2, xt_1, xt_2;

	/** Cutoff frequency in Hertz. */
	protected float f;

	/** Coefficients (Steiglitz notation). */
	protected float cc, dd;

	/** Gain. A0 is normalized. A is actual gain, gain is user settable */
	protected float A = 1.f, A0 = 1.f, gain = 1;

	/**
	 * Set the filter cutoff.
	 * 
	 * @param f
	 *            cutoff frequency in Hertz.
	 */
	public synchronized void setCutoffFrequency(float f) {
		if(getContext()==null){
			setProperty("cutoffFreq", Float.toString(f));
			return;
		}
		float tt = (float) (Math.sqrt(2) / Math.tan(2 * Math.PI * f / getContext().getSampleRate()));
		float tp = tt + 1, tm = tt - 1;
		float den = tp * tp + 1;
		float re = (tm * tp - 1) / den;
		float im = 2 * tt / den;
		cc = -2 * re;
		dd = re * re + im * im;
		A0 = (1 + cc + dd) / 4;
		A = gain * A0;
	}

	/**
	 * Set gain.
	 * 
	 * @param gain
	 *            gain.
	 */
	public synchronized void setGain(float gain) {
		A = gain * A0;
		this.gain = gain;
	}

	/**
	 * Reset state.
	 */
	public synchronized void reset() {
		int channels = getContext().getInputChannelCount();
		yt_1 = new float[channels];
		yt_2 = new float[channels];
		xt_1 = new float[channels];
		xt_2 = new float[channels];
	}

	@Override
	public synchronized void configure(AudioConfiguration context) throws Exception {
		super.configure(context);
		reset();
		if(getProperty("cutoffFreq")!=null){
			float f = Float.parseFloat(getProperty("cutoffFreq"));
			setCutoffFrequency(f);
		}
	}

	@Override
	public synchronized void resetTime(long t) {
		super.resetTime(t);
		reset();
	}

	@Override
	protected void executeCommand(long timeStamp, String command) {
		// TODO Auto-generated method stub

	}

	@Override
	protected void computeBuffer() {
		int channels = getContext().getInputChannelCount();
		int nsamples = getContext().getMaxBufferSize();
		FloatSampleBuffer fsb = new FloatSampleBuffer(channels,nsamples,getContext().getSampleRate());
		for(Source src:getSources()){
			try {
				fsb.mix(src.getBuffer(getTime()));
			} catch (BufferNotAvailableException e) {
				continue;
			}
		}
		for(int i=0;i<channels;i++){
            for(int k=0;k<nsamples;k++) {
                float ynew = fsb.getAllChannels()[i][k] + 2*xt_1[i] + xt_2[i] - cc*yt_1[i] - dd*yt_2[i];
                yt_2[i] = yt_1[i];
                yt_1[i] = ynew;
                xt_2[i] = xt_1[i];
                xt_1[i] = fsb.getAllChannels()[i][k];
                buf.getAllChannels()[i][k] = A*ynew;
            }
		}
	}

}
